/*
 * OutMessage.java
 *
 * Created on September 3, 2007, 11:35 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */


/*
*Pool Public Licence version 0.1 or later
*Released on 20th April 2008.
*Any voilations can be repoted at paritosh@wikiocean.net
*What is treated as voilations can be found at www.wikiocean.net/ppl1-voilations* 
******************************************************************************
* The contents of this file are subject Affero GPL v3 and later with additional conditions.
* The additional conditions are to be considered as part of the Affero GPL v3 and later.
* The additional conditions will supersede the Affero GPLv3 and later in case of conflict.
* ("License"); You may not use this file except in compliance with the License
* You may obtain a copy of the License at http://www.gnu.org/licenses/agpl.html
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
* The Initial Developer of the Original Code is Paritosh Pungaliya.
* created by Paritosh Pungaliya are Copyright (C) Paritosh Pungaliya; All Rights Reserved.
* Contributions are Copyright (C) 2001-2008 Paritosh Pungaliya.

* Objective of the additional terms (license) 
* 1) Is to extend the software freedom to freedom to work.
* 2) To ensure that knowledge is free from monopoly of agencie(s).
* 3) To avoid a situation where big corporates or investors can buy out free software groups and companies and then start milking the communities built around it.
* (this trend can be seen how the openness changes in companies that get Venture Capital funding.)
* (if you agree to this logic I invite you to our group to work together)

* Additional conditions
*  0) Source code will also include
* 	a) data dictionaries, data ontologies which are used to alter the behavior of code or to control           the features of application.
* 	b) Any code generated or derived automatically or manually from this code or logic
*  1) The revenue generated by deploying, implementing , selling services and other activities
*  	 based on this source code should be shared 100%, between the people working on it.
*  2) Capital invested should be given only fixed rate of return or linked to revenue/surplus growth for limited time.
*  3) People working on the project should always have an option to replace the capital provider in full or part with lower cost capital.
*  4) Their should not be any ownership which can be sold as a claim on future profit to be generated from the work of people working on the code.
*  5) Brand thus generated should belong to the group.
*  6) For old and new members revenue sharing should be equal for same work equivalent.
*  7) The share of revenue should be decided by bidding for work before allocation of work within the group.
*  8) Before bidding various method of measuring the work should be clearly defined and published in public domain. Any change in process should also be publised in public domain imediately.
*  9) All data related to to revenue sharing should be published as and when generated in public domain.
*  10) For group of people having size more than 10 people will have to make it open online for others to participate in bids. 
*  11) All people involved in related and sub project will be treated as a group.
*  12) Once this license is being used for a certain code then all and any other software services being provided by the group should complusorilly come under 100% revenue sharing.
*  13) Cross subsidization should not happen under this revenue share, no additional benefit should be drawn from the people, by the employing agency.
*  14) Any position or knowledge or legal structure created relating to people working on code, which adversely affects the spirit of 100% revenue share will be treated as a violation.
*  15) Revenue sharing defined.
*     a) includes direct revenue and also intangible gains like brand, and organization.
*     b) No personal judgment should be involved in judging the distribution of revenue.It has to be pre-defined formula based.
*     c)100% of revenue should be shared before expenses and recievers should have control over what expenses to incur.
*     d)For the work done in any month by contributing group member, working on any project can be given revenue share from the work done by the group, for a maximum of 18 months after that month.
*     e)Revenue should be apportioned to the month in which work is done and not when received.
*  The group fulfilling the above conditions also has to be a wiki ecosystem further defined in www.wekosystem.org or read about Wekosystem at www.wikiocean.net

*  ******************************************************************************
*/  
package SMS;

import java.util.*;

import combinereport.query.In;


/**
 * OutMessage objects are the messages that will be sent through the GSM Modem. 
 * These objects need to be initilised before calling the send method on it.
 * @author Administrator
 */
public class OutMessage 
{
    private static int maxSize = 160;
    private int srcPort = -1;
    private int dstPort = -1;
    private int validityPeriod = -1;
    private boolean statusReport = true;
    protected Date dispatchDate;
    protected MessageEncoding messageEncoding;
    protected int refNo=-1;
    private int outMpRefNo;
    
    private int mpRefNo=0, partNo=0;
    public static enum MessageEncoding { Enc7Bit, Enc8Bit, EncUcs2 }
    
    String sms;
    String no;
    
    /** Creates a new instance of OutMessage */
    public OutMessage(String no, String msg)
    {
    	sms = msg;
		this.no = no;
		
        this.dispatchDate = null;
	
	        outMpRefNo = new Random().nextInt();
		if (outMpRefNo < 0) 
	            outMpRefNo *= -1;
		outMpRefNo %= 65536;
		
    }
    
    /** Used to check if the message to be sent is greater than 160 chars (ie. big) */
    protected boolean isBig() throws Exception
    {
        int messageLength;
        messageLength = sms.length();
        return (messageLength > maxSize ? true : false);
    }
    
    /** 
     * Converts the message to be sent in the PDU format. Adds the reference code to it. 
     * Handles the big message situation.
     */
    public String getPDU(String smscNumber, int mpRefNo, int partNo) throws Exception
    {
            String pdu, udh;
            String str1, str2;
            int i, high, low;
            char c;

            pdu = "";
            udh = "";
            if ((smscNumber != null) && (smscNumber.length() != 0))
            {
                    str1 = "91" + toBCDFormat(smscNumber.substring(1));
                    str2 = Integer.toHexString(str1.length() / 2);
                    if (str2.length() != 2) str2 = "0" + str2;
                    pdu = pdu + str2 + str1;
            }
            else if ((smscNumber != null) && (smscNumber.length() == 0)) pdu = pdu + "00";
            if (((srcPort != -1) && (dstPort != -1)) || (isBig()))
            {
                    if (statusReport) pdu = pdu + "71";
                    else pdu = pdu + "51";
            }
            else
            {
                    if (statusReport) pdu = pdu + "31";
                    else pdu = pdu + "11";
            }
            pdu = pdu + "00";
            str1 = getRecipient();
            if (str1.charAt(0) == '+')
            {
                    str1 = toBCDFormat(str1.substring(1));
                    str2 = Integer.toHexString(getRecipient().length() - 1);
                    str1 = "91" + str1;
            }
            else
            {
                    str1 = toBCDFormat(str1);
                    str2 = Integer.toHexString(getRecipient().length());
                    str1 = "81" + str1;
            }
            if (str2.length() != 2) str2 = "0" + str2;

            pdu = pdu + str2 + str1;
            pdu = pdu + "00";
            switch (getMessageEncoding())
            {
                    case Enc7Bit:
                                pdu = pdu + "00";
                            break;
                    
                    default: throw new Exception();
            }

            pdu = pdu + getValidityPeriodBits();

            if ((srcPort != -1) && (dstPort != -1))
            {
                    String s;

                    udh += "060504";
                    s = Integer.toHexString(dstPort);
                    while (s.length() < 4)
                            s = "0" + s;
                    udh += s;
                    s = Integer.toHexString(srcPort);
                    while (s.length() < 4)
                            s = "0" + s;
                    udh += s;
            }

            if (isBig())
            {
                    String s;

                    if ((srcPort != -1) && (dstPort != -1)) udh = "0C" + udh.substring(2) + "0804";
                    else udh += "060804";
                    s = Integer.toHexString(mpRefNo);
                    while (s.length() < 4)
                            s = "0" + s;
                    udh += s;
                    s = Integer.toHexString(getNoOfParts());
                    while (s.length() < 2)
                            s = "0" + s;
                    udh += s;
                    s = Integer.toHexString(partNo);
                    while (s.length() < 2)
                            s = "0" + s;
                    udh += s;
            }

            switch (getMessageEncoding())
            {
                    case Enc7Bit:
                            str2 = textToPDU(getPart(sms, partNo));
                            i = CGSMAlphabet.stringToBytes(getPart(sms, partNo), new byte[400]);
                            if ((srcPort != -1) && (dstPort != -1)) str1 = Integer.toHexString(i + 8);
                            else if (isBig()) str1 = Integer.toHexString(i + 8);
                            else str1 = Integer.toHexString(i);
                            break;
                            
                    default:
                            throw new Exception();
            }
            if (str1.length() != 2) str1 = "0" + str1;
            if (((srcPort != -1) && (dstPort != -1)) || (isBig())) pdu = pdu + str1 + udh + str2;
            else pdu = pdu + str1 + str2;
            return pdu.toUpperCase();
    }

    /** Converts the String to the BCD Format */
    private String toBCDFormat(String s)
    {
            String bcd;
            int i;

            if ((s.length() % 2) != 0) s = s + "F";
            bcd = "";
            for (i = 0; i < s.length(); i += 2)
                    bcd = bcd + s.charAt(i + 1) + s.charAt(i);
            return bcd;
    }
    
    /** Used to get the recipitent identfication number */
    public String getRecipient() 
    { 
        return no;
    }
    
    /** Return the validity period. */
    private String getValidityPeriodBits()
    {
            String bits;
            int value;

            if (validityPeriod == -1) bits = "FF";
            else
            {
                    if (validityPeriod <= 12) value = (validityPeriod * 12) - 1;
                    else if (validityPeriod <= 24) value = (((validityPeriod - 12) * 2) + 143);
                    else if (validityPeriod <= 720) value = (validityPeriod / 24) + 166;
                    else value = (validityPeriod / 168) + 192;
                    bits = Integer.toHexString(value);
                    if (bits.length() != 2) bits = "0" + bits;
                    if (bits.length() > 2) bits = "FF";
            }
            return bits;
    }

    /**
     * In case of long message this is used to get the number of messages that the 
     * long message should be splited in. 
     */ 
    protected int getNoOfParts() 
    {
            int noOfParts = 0;
            int partSize;
            int messageLength;

            partSize = maxSize - 8;
            messageLength = sms.length();
            
            noOfParts = messageLength / partSize;
            if ((noOfParts * partSize) < (messageLength)) noOfParts++;
            return noOfParts;
    }

    /** Converts the text to be sent in the pdu format */
    private String textToPDU(String txt) throws Exception
    {
            String pdu, str1;
            byte[] bytes, oldBytes, newBytes;
            BitSet bitSet;
            int i, j, value1, value2;

            bytes = new byte[400];
            i = CGSMAlphabet.stringToBytes(txt, bytes);
            oldBytes = new byte[i];
            for (j = 0; j < i; j++)
                    oldBytes[j] = bytes[j];
            bitSet = new BitSet(oldBytes.length * 8);

            value1 = 0;
            for (i = 0; i < oldBytes.length; i++)
                    for (j = 0; j < 7; j++)
                    {
                            value1 = (i * 7) + j;
                            if ((oldBytes[i] & (1 << j)) != 0) bitSet.set(value1);
                    }
            value1++;

            if (((value1 / 56) * 56) != value1) value2 = (value1 / 8) + 1;
            else value2 = (value1 / 8);
            if (value2 == 0) value2 = 1;

            newBytes = new byte[value2];
            for (i = 0; i < value2; i++)
                    for (j = 0; j < 8; j++)
                            if ((value1 + 1) > ((i * 8) + j)) if (bitSet.get(i * 8 + j)) newBytes[i] |= (byte) (1 << j);

            pdu = "";
            for (i = 0; i < value2; i++)
            {
                    str1 = Integer.toHexString(newBytes[i]);
                    if (str1.length() != 2) str1 = "0" + str1;
                    str1 = str1.substring(str1.length() - 2, str1.length());
            //        str1 = String.valueOf(Integer.parseInt(str1, 16));
                    pdu += str1;
            }
            //String temp = String.valueOf(Integer.parseInt(pdu, 16));
            return pdu;
    }
    
    /** Returns the part requested, of the long messages. Splits the long message*/
    private String getPart(String txt, int partNo) throws Exception
    {
            String textPart;
            int partSize;

            textPart = txt;
            if (partNo != 0)
            {
                    partSize = maxSize - 8;
                    if (((partSize * (partNo - 1)) + partSize) > txt.length()) textPart = txt.substring(partSize * (partNo - 1));
                    else textPart = txt.substring(partSize * (partNo - 1), (partSize * (partNo - 1)) + partSize);
            }
            return textPart;
    }
    
    void processBigSMS() throws Exception
    {
    	String pdu=null;
    	 int j;
       
    	 Modem modem = new Modem("COM4","115200");
        if (!isBig()) // for small message.
        {
        	try 
        	{
				pdu = getPDU(no, 0, 0);
			}
        	catch (Exception e) {
				e.printStackTrace();
			}
			j = pdu.length();
			j /= 2;
        
			if (no == null) ; // Do nothing on purpose!
			else if (no.length() == 0) 
				j--;
			else
            {
	            int smscNumberLen = no.length();
	            if (no.charAt(0) == '+') smscNumberLen--;
	            if (smscNumberLen % 2 != 0) smscNumberLen++;
	            int smscLen = (2 + smscNumberLen) / 2;
	            j = j - smscLen - 1;
            }
        }
		else
		{
			   System.out.println("Sending Long Message. In "+getNoOfParts()+" parts.");
			   
	        for (int partNo = 1; partNo <= getNoOfParts(); partNo++)
	        {
	        	
	                System.out.println("__ Sending part "+partNo+" __");
	                try 
	                {
						pdu = getPDU(no,mpRefNo, partNo);
					} 
	                catch (Exception e) {
						e.printStackTrace();}
					
	                j = pdu.length();
	                j /= 2;
	                if (no == null) ; // Do nothing on purpose!
	                else if (no.length() == 0) j--;
	                else
	                {
	                        int smscNumberLen = no.length();
	                        if (no.charAt(0) == '+') smscNumberLen--;
	                        if (smscNumberLen % 2 != 0) smscNumberLen++;
	                        int smscLen = (2 + smscNumberLen) / 2;
	                        j = j - smscLen - 1;
	                }
	                //synchronized (_SYNC_) 
	                //{ 
	                //pdu = hexToString(pdu);
	                	System.out.println("Message index ::"+ j +"\n Message to Send :"+pdu);
	                	modem.SendSms("+919922930640", pdu, 2);
	                	
	                	//refNo = atHandler.sendMessage(j, pdu); 
	                //}
	              // Thread.sleep(2500);
	        }
	        outMpRefNo = (outMpRefNo + 1) % 65536;
		}
    }
    public String hexToString(String txtInHex) 
    {
        byte [] txtInByte = new byte [txtInHex.length() / 2];
        int j = 0;
        for (int i = 0; i < txtInHex.length(); i += 2)
        {
        		String str = txtInHex.substring(i, i + 2);
                txtInByte[j++] = Byte.parseByte(str, 16);
        }
        String txt = new String(txtInByte);
        System.out.println(txt);
        return txt;
    }

    public MessageEncoding getMessageEncoding() 
    { 
        return messageEncoding; 
    }
    public void setMessageEncoding(MessageEncoding messageEncoding) 
    { 
        this.messageEncoding = messageEncoding; 
    }
    protected void setRefNo(int refNo) 
    { 
        this.refNo = refNo; 
    }
    
    public static void main(String[] args) {
		try 
		{
			OutMessage sm = new OutMessage("+919922930640","afhghdflhglhfghkldfhghdfklhg dhshgkldgfhkj hgdhfhg ldfhgyre sdhgfhf ghgfjasd sudfhsg asdhg asdguih" +
					"dfhgfhoitogih dfhgif ghigfgf[hgf higfh ] hkf gihpigfhopigh idoifsejrkj  dfgidf gj kgyhpfk h oyirotritiyitr kl dgfh");
			sm.setMessageEncoding(MessageEncoding.Enc7Bit);
			
			sm.processBigSMS();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}       
